#ifndef __M_HELPER_H
#define __M_HELPER_H

#pragma once
#include<sqlite3.h>
#include<string>
#include<vector>
#include<iostream>
#include<fstream>
#include"mq_logger.hpp"
#include<random>
#include<sstream>
#include<iomanip>
#include<cstring>
#include<cerrno>
#include<atomic>
#include<sys/stat.h>

namespace xiang11mq
{

class SqliteHelper
{
public:
    typedef int (*SqliteCallback)(void *, int, char **, char **); // 定义一个函数指针类型
    SqliteHelper(const std::string &dbfile)
        : _dbfile(dbfile), _handler(nullptr)
    {
    }
    bool open(int safe_leve = SQLITE_OPEN_FULLMUTEX)
    {
        // int sqlite3_open_v2(const char *filename, sqlite3 **ppDb, int flags, const char *zVfs );
        // 以可读可写方式打开数据库 没文件就创建 安全等级串行化
        int ret = sqlite3_open_v2(_dbfile.c_str(), &_handler, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | safe_leve, nullptr);
        if (ret != SQLITE_OK)
        {
            ELOG("创建/打开sqlite 数据库失败 : %s", sqlite3_errmsg(_handler));
            return false;
        }
        return true;
    }

    bool execute(const std::string &sql, SqliteCallback cb, void *arg) // sql 语句 cb回调函数 arg缓冲区
    {
        // int sqlite3_exec(sqlite3*, char *sql, int (*callback)(void*,int,char**,char**), void* arg, char **err)
        // int (*callback)(void*,int,char**,char**)
        // void* arg: 是设置的在回调时传⼊的arg参数
        // int：⼀⾏中数据的列数
        // char**：存储⼀⾏数据的字符指针数组
        // char**：每⼀列的字段名称
        int ret = sqlite3_exec(_handler, sql.c_str(), cb, arg, nullptr);
        if (ret != SQLITE_OK)
        {
            ELOG("%s \n执行语句失败 : %s", sql.c_str(), sqlite3_errmsg(_handler));
            return false;
        }

        return true;
    }
    void close()
    {
        // int sqlite3_close(sqlite3* db); 成功返回SQLITE_OK
        if (_handler)
            sqlite3_close_v2(_handler);
    }

private:
    std::string _dbfile;
    sqlite3 *_handler;
};

class StrHelper
{
public:
    static size_t split(const std::string &str, const std::string &sep, std::vector<std::string> &result)
    {
        size_t pos = 0, idx = 0;
        while(idx < str.size())
        {
            pos = str.find(sep, idx);
            if(pos == std::string::npos)
            {
                //没有找到---》 没有或者走到头了
                result.push_back(str.substr(idx));
                return result.size();
            }

            if(pos == idx) //分隔符之间没有数据
            {
                idx = pos + sep.size();   
                continue;
            }
            
            result.push_back(str.substr(idx, pos - idx));
            idx = pos + sep.size();   
        }

        return result.size();

    }

private:
};

class UUIDHelper
{
public:
    static std::string uuid()
    {
        std::random_device rd;
        std::mt19937_64 gernator(rd());
        // 需要8个0 到255之间的数字 要限定数字区间
        std::uniform_int_distribution<int> distribution(0, 255);
        // 将生成的随机数转换位16进制数字字符
        std::stringstream ss;
        for (int i = 0; i < 8; i++)
        {
            // 可能出现小于16 单字符情况 所以设置位宽2 高位填充0
            ss << std::setw(2) << std::setfill('0') << std::hex << distribution(gernator);
            // 8-4-4
            if (i == 3 || i == 5 || i == 7)
                ss << "-";
        }
        static std::atomic<size_t> seq(1); // 定义一个原子类型整数序列号 初始化为1
        size_t num = seq.fetch_add(1);     // 前置++
        for (int i = 7; i >= 0; i--)
        {
            //   & 0000000000001111111111111
            ss << std::setw(2) << std::setfill('0') << std::hex << ((num >> i * 8) & 0xff);
            // 8-4-4-   4-12
            if (i == 6)
                ss << "-";
        }
        return ss.str();
    }
};

class FileHelper
{
public:
    FileHelper(const std::string &filename)
        :_filename(filename)
    {}

    bool exists()   //判断文件是否存在
    {
        struct stat st;
        return (stat(_filename.c_str(), &st) == 0);   //=0存在 =-1 不存在
    }
    size_t size()   //读取文件大小
    {
        struct stat st;
        int ret = stat(_filename.c_str(), &st);
        if(ret < 0)
            return 0;
        return st.st_size;
    }
    bool read(std::string &body) //读取文件全部内容
    {
        //获取文件大小 根据文件大小调整body的空间
        size_t fsize = this->size();
        body.resize(fsize);
        return read(&body[0], 0, fsize);

    }
    bool read(char *body, size_t offset, size_t len) //读取部分数据  offset偏移量
    {
        if(body == nullptr)
        {
            ELOG("读取时body为空");
            return false;
        }
        //1. 打开文件   二进制打开 文本打开\n\t可能出问题
        std::ifstream ifs(_filename, std::ios::binary | std::ios::in);
        if(ifs.is_open() == false)
        {
            ELOG("%s 文件读取时打开失败", _filename.c_str());
            return false;
        }
        //2. 跳转文件读写位置
        ifs.seekg(offset, std::ios::beg);    //偏移量 基地址begin
        //3. 关闭文件数据
        ifs.read(body, len);
        if(ifs.good() == false)
        {
            ELOG("%s 文件读取数据失败", _filename.c_str());
            ifs.close();
            return false;
        }
        //4. 关闭文件
        ifs.close();
        return true;

    }
    bool write(const std::string &body) //const& 输入型参数 向文件写数据
    {
        return write(body.c_str(), 0, body.size());
    }
    bool write(const char *body, size_t offset, size_t len) //将body里面内容写到文件
    {
        if (body == nullptr)
        {
            ELOG("写时body为空");
            return false;
        }
        // 1. 打开文件   二进制打开 文本打开\n\t可能出问题
        //不用ofs 因为没有std::ios::in seekp跳转
        std::fstream fs(_filename, std::ios::binary | std::ios::in | std::ios::out);
        if (fs.is_open() == false)
        {
            ELOG("%s 文件写入时打开失败", _filename.c_str());
            return false;
        }
        // 2. 跳转文件读写位置
        fs.seekp(offset, std::ios::beg); // 偏移量 基地址begin
        // 3. 关闭文件数据
        fs.write(body, len);
        if (fs.good() == false)
        {
            ELOG("%s 文件写入数据失败", _filename.c_str());
            fs.close();
            return false;
        }
        // 4. 关闭文件
        fs.close();
        return true;
    }
    bool rename(const std::string &nname)
    {
        return (::rename(_filename.c_str(), nname.c_str()) == 0);
    }
    static bool createFile(const std::string &filename)   //创建文件
    {
        std::ofstream ofs(filename, std::ios::binary | std::ios::out);
        if (ofs.is_open() == false)
        {
            ELOG("%s 创建文件失败", filename.c_str());
            return false;
        }
        ofs.close();
        return true;
    }
    static bool removeFile(const std::string &filename)   //删除文件
    {
        return (::remove(filename.c_str()) == 0);
    }
    static bool createDirectory(const std::string &path)   //创建目录
    {
        //    aaa/bbb/ccc/ddd
        //多级路径创建过程 需要从第一个父级目录开始创建
        size_t pos, idx = 0; 
        while(idx < path.size())
        {
            pos = path.find("/", idx);
            if(pos == std::string::npos)
            {
                //  test or aaa/bbb/ccc/ddd
                return (mkdir(path.c_str(), 0775) == 0);   //权限 其他用户只能看
            }
            //文件创建 起始位置是0 不是idx
            std::string subpath = path.substr(0, pos);
            int ret = mkdir(subpath.c_str(), 0775);
            if(ret != 0 && errno != EEXIST)     //错误不是文件已存在
            {
                ELOG("创建目录 %s 失败 : %s", subpath.c_str(), strerror(errno));
                return false;
            }
            idx = pos + 1;
        }
        return true;
    }
    static bool removeDirectory(const std::string &path)  //删除目录
    {
        //rmdir 删除时需要目录里没东西 需要递归式删除 太麻烦
        std::string cmd = "rm -rf " + path;
        return (system(cmd.c_str()) != -1);
    }
    static std::string parentDirectory(const std::string &filename) //获取filename 的父级目录
    {
        //        /dwa/dwadaaa/gh/jyjgk
        //函数用于从字符串的末尾开始向前搜索，查找与指定字符集中的任何一个字符相匹配的最后一个字符的位置
        size_t pos = filename.find_last_of("/");
        if(pos == std::string::npos)
        {
            //    text.txt  这种没有/
            return "./";
        }
        std::string path = filename.substr(0, pos);
        return path;
    }
private:
    std::string _filename;
};



}


#endif